---
title: DeleteUserFromGroup
summary: Removes an external-domain user from a group via the underlying DirectoryEntry, gated by the IP allow-list.
status: draft
owner: "Not determined from source"
last_reviewed: 2026-09-22
---

# DeleteUserFromGroup (OP-06)

## Request and response

| Part | Type | Description |
| --- | --- | --- |
| `inUserId` | `String` | The `SamAccountName` of the user to remove from the group. |
| `inGroupName` | `String` | The `SamAccountName` of the target group. |

| Part | Type | Description |
| --- | --- | --- |
| `<return>` | `ADServiceResponse` | `Response` is `Success` (including on a swallowed exception) or `GroupDoesNotExist`. `Message` is always empty on this operation. |

## WebMethod attributes

`<WebMethod()>` — bare, no arguments supplied.

## Steps

1. `CheckAccess()`; on denial return `AccessDenied()` ([BR-01](../business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller)).
2. Initialize the response sentinel to `Success`.
3. Open `Try`.
4. Call H2 `GetUserPrincipal` for the external domain.
   - If `Nothing`, fall through to step 8 leaving `Response = Success`.
5. `deUser = userPrin.GetUnderlyingObject`.
6. Call H3 `GetGroupPrincipal` for the external domain.
   - If `Nothing`, set `Response = GroupDoesNotExist`.
7. Else `deGroup = groupPrin.GetUnderlyingObject`; call `deGroup.Invoke("Remove", New Object() {deUser.Path})` — the code drops to the underlying `DirectoryEntry` deliberately, per the authored comment "Need to go down to DirectoryEntry because the groupPrincipal Remove method fails on large groups" ([BR-19](../business-rules.md#br-19-group-removal-drops-to-the-underlying-directoryentry)); then `deGroup.CommitChanges()`.
8. `Catch ex As Exception`: the exception is **swallowed** — the authored comment reads "gobble - user is probably not in the group" ([BR-20](../business-rules.md#br-20-a-failed-group-removal-is-reported-as-success)). This is tech debt TD-20 (HIGH — see [Tech debt](../tech-debt.md)): a genuine directory failure is indistinguishable from a completed removal.
9. `Finally` releases `deGroup`, `deUser`, `groupPrin`, and `userPrin`.
10. Return the response sentinel.

## Process flow

```mermaid
flowchart TD
    A["Call DeleteUserFromGroup"] --> B{"CheckAccess<br>HasAccess?"}
    B -->|"No"| C["Return AccessDenied"]
    B -->|"Yes"| D["GetUserPrincipal<br>external domain"]
    D -->|"Nothing"| E["Fall through<br>leave Success"]
    D -->|"Found"| F["GetGroupPrincipal<br>external domain"]
    F -->|"Nothing"| G["Set GroupDoesNotExist"]
    F -->|"Found"| H["DirectoryEntry.Invoke Remove<br>then CommitChanges"]
    H -.->|"Any exception"| I["Swallow exception<br>leave Success"]
    E --> J["Return response"]
    G --> J
    H --> J
    I --> J
```

## Shared state effects

None — this operation touches no process-wide state; all locals are per-request.

## Error handling

The operation always returns a sentinel `ADServiceResponse`; it never surfaces a framework SOAP fault. Because the `Catch` block is empty, it returns `Success` for a genuine removal, for "user was not a member", and for a directory failure alike (tech debt TD-20). See [Faults](../faults.md) for the service-wide fault model.

## Authorization

Gated by [BR-01](../business-rules.md#br-01-mutating-and-password-operations-require-an-approved-caller): the caller must pass the IP allow-list check enforced by `CheckAccess`. See [Authentication and authorization](../authentication-authorization.md).

## Related documentation
- [Service index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Activation and hosting](../activation-hosting.md)
- [Operations](../operations.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [Authentication and authorization](../authentication-authorization.md)
- [Configuration](../configuration.md)
- [Dependencies](../dependencies.md)
- [Faults](../faults.md)
- [Operational runbook](../operational-runbook.md)
- [Tech debt](../tech-debt.md)

## Sources
- `src\ADUserServices\clsADUserServices.asmx.vb` (lines 297-340)
